package com.cloudeasy.area.service;

import com.cloudeasy.api.enums.ResultCodeEnum;
import com.cloudeasy.api.exception.CloudEasyException;
import com.cloudeasy.api.util.SnowFlake;
import com.cloudeasy.area.constant.AreaConstant;
import com.cloudeasy.area.dto.AreaDTO;
import com.cloudeasy.area.po.Area;
import com.cloudeasy.area.repository.AreaRepository;
import org.springframework.beans.BeanUtils;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Optional;

@Service
@Transactional(rollbackFor = Exception.class)
public class AreaService {
    private final AreaRepository areaRepository;

    private final AreaRelyService areaRelyService;

    private final AreaAttentionService areaAttentionService;

    private final MongoTemplate mongoTemplate;

    private final SnowFlake snowFlake;

    public AreaService(AreaRepository areaRepository, @Lazy AreaRelyService areaRelyService,@Lazy AreaAttentionService areaAttentionService, MongoTemplate mongoTemplate, SnowFlake snowFlake) {
        this.areaRepository = areaRepository;
        this.areaRelyService = areaRelyService;
        this.areaAttentionService = areaAttentionService;
        this.mongoTemplate = mongoTemplate;
        this.snowFlake = snowFlake;
    }

    /**
     * 添加区域
     *
     * @param areaDTO {@link AreaDTO}
     * @return {@link Area}
     */
    public Area save(AreaDTO areaDTO) {
        if (areaDTO.getName().equals(AreaConstant.GLOBAL_ROOT_AREA_NAME)) {
            throw new CloudEasyException(ResultCodeEnum.NAME_EXISTS);
        }
        // 如果没有记录
        if (areaRepository.count() == 0) {
            // 添加root
            areaRepository.save(Area.builder()
                    .id(AreaConstant.GLOBAL_ROOT_AREA_ID)
                    .name("root")
                    .description("根区域，没必要关注了吧？")
                    .status(AreaConstant.NORMAL)
                    .build());
            areaRelyService.init();
        }
        if (areaRepository.existsByName(areaDTO.getName())) {
            throw new CloudEasyException(ResultCodeEnum.NAME_EXISTS);
        }
        Area area = new Area();
        BeanUtils.copyProperties(areaDTO, area);
        area.setId(String.valueOf(snowFlake.nextId()))
                .setAttentionCount(0)
                .setItemCount(0)
                .setStatus(AreaConstant.NORMAL);
        Area save = areaRepository.save(area);
        // 插入成功后，默认分配为根节点
        areaRelyService.save(AreaConstant.GLOBAL_ROOT_AREA_ID, area.getId());
        return save;
    }

    /**
     * 更新区域信息
     *
     * @param id      区域Id
     * @param areaDTO {@link AreaDTO}
     * @return {@link Area}
     */
    public Area update(String id, AreaDTO areaDTO) {
        Optional<Area> optionalArea = areaRepository.findById(id);
        if (!optionalArea.isPresent()) {
            throw new CloudEasyException(ResultCodeEnum.ID_NOT_EXISTS);
        }
        Area area = optionalArea.get();
        if (!area.getName().equals(areaDTO.getName()) &&
                areaRepository.existsByName(areaDTO.getName())) {
            throw new CloudEasyException(ResultCodeEnum.NAME_EXISTS);
        }
        BeanUtils.copyProperties(areaDTO, area);
        return areaRepository.save(area);
    }

    /**
     * 删除区域
     *
     * @param id Id
     */
    public void delete(String id) {
        if (id.equals(AreaConstant.GLOBAL_ROOT_AREA_ID)) {
            return;
        }
        if (!areaRepository.existsById(id)) {
            throw new CloudEasyException(ResultCodeEnum.ID_NOT_EXISTS);
        }
        areaRepository.deleteAreaByIdAndItemCountEquals(id, 0);
        if (areaRepository.existsById(id)) {
            throw new CloudEasyException(ResultCodeEnum.AREA_NOT_NULL);
        }

        areaAttentionService.deleteAttention(id);
        // 删除成功，清除节点
        areaRelyService.delete(id);
    }

    /**
     * 查询区域列表
     *
     * @return {@link List<Area>}
     */
    public List<Area> list() {
        return areaRepository.findAll();
    }

    /**
     * 根据Id列表查询区域列表
     *
     * @param ids Id列表
     * @return {@link List<Area>}
     */
    public List<Area> listByIds(List<String> ids) {
        return areaRepository.findAllByIdInOrderByItemCount(ids);
    }

    /**
     * 根据名称查询区域列表
     *
     * @param name 名称
     * @return {@link List<Area>}
     */
    public List<Area> listByName(String name) {
        return areaRepository.findAllByNameContainingOrderByItemCount(name);
    }

    /**
     * 分页查询区域列表
     *
     * @param page 页数
     * @param size 每页大小
     * @return {@link Page<Area>}
     */
    public Page<Area> page(Integer page, Integer size) {
        return areaRepository.findAll(PageRequest.of(page - 1, size));
    }

    /**
     * 根据Id列表查询区域列表
     *
     * @param ids  Id列表
     * @param page 页数
     * @param size 每页大小
     * @return {@link Page<Area>}
     */
    public Page<Area> pageByIds(List<String> ids, Integer page, Integer size) {
        return areaRepository.findAllByIdInOrderByItemCount(ids, PageRequest.of(page - 1, size));
    }

    /**
     * 根据名称查询区域列表
     *
     * @param name 名称
     * @param page 页数
     * @param size 每页大小
     * @return {@link Page<Area>}
     */
    public Page<Area> pageByName(String name, Integer page, Integer size) {
        return areaRepository.findAllByNameContainingOrderByItemCount(name, PageRequest.of(page - 1, size));
    }

    /**
     * 被关注，关注数+1
     *
     * @param areaId 区域Id
     */
    public void attention(String areaId) {
        if (!areaRepository.existsById(areaId)) {
            throw new CloudEasyException(ResultCodeEnum.ID_NOT_EXISTS);
        }
        attentionInc(areaId, 1);
    }

    /**
     * 移除关注，关注数-1
     *
     * @param areaId 区域Id
     */
    public void removeAttention(String areaId) {
        if (!areaRepository.existsById(areaId)) {
            throw new CloudEasyException(ResultCodeEnum.ID_NOT_EXISTS);
        }
        attentionInc(areaId, -1);
    }

    /**
     * 关注数更新
     *
     * @param areaId 区域Id
     * @param inc    自增值
     */
    public void attentionInc(String areaId, Integer inc) {
        Query query = new Query();
        query.addCriteria(Criteria.where("id").is(areaId));
        Update update = new Update();
        update.inc("attentionCount", inc);
        mongoTemplate.updateFirst(query, update, Area.class);
    }

    /**
     * 项数更新
     *
     * @param areaName 区域名称
     * @param inc      自增值
     */
    public void itemIncByAreaName(String areaName, Integer inc) {
        if (areaName.equals(AreaConstant.GLOBAL_ROOT_AREA_NAME)) {
            return;
        }
        Query query = new Query();
        query.addCriteria(Criteria.where("name").is(areaName));
        Update update = new Update();
        update.inc("itemCount", inc);
        mongoTemplate.updateFirst(query, update, Area.class);
    }
}
